﻿/**
 * @file		dd_pmc_watchdog.c
 * @brief		Watchdog Timer driver
 * @note		None
 * @attention	None
 * 
 * <B><I>Copyright 2016 Socionext Inc.</I></B>
 */

#include "pmc.h"
#include "dd_pmc_watchdog.h"
#include "dd_pmc_crg.h"
#include "dd_arm.h"

/*----------------------------------------------------------------------*/
/* Definition															*/
/*----------------------------------------------------------------------*/
/* Initialize of Watchdog Load (32bit) */
#define D_DD_PMC_WDOG_LOAD_INIT					(0xFFFFFFFF)
/* Max value of Watchdog Counter (32bit) */
#define D_DD_PMC_WDOG_COUNTER_MAX_VALUE			(0xFFFFFFFF)
/* Max value of Watchdog Prescaler (8bit) */
#define D_DD_PMC_WDOG_PRESCALER_MAX_VALUE		(0xFF)

/* Watchdog Lock Register - enables write access to all other registers */
#define D_DD_PMC_WDOG_REGISTER_ACCESS_ENABLE	(0x1ACCE551)

/* PLL Clock */
#define D_DD_PMC_WDOG_PLL_CLOCK_100MHZ			(100000000)			/**< PLL Clock:100[MHz] */

/*----------------------------------------------------------------------*/
/* Enumeration															*/
/*----------------------------------------------------------------------*/
//* Nothing Special

/*----------------------------------------------------------------------*/
/* Structure															*/
/*----------------------------------------------------------------------*/
// Nothing Special

/*----------------------------------------------------------------------*/
/* Global Data															*/
/*----------------------------------------------------------------------*/
static volatile ULONG gDD_PMC_Wdog_Soft_Counter = 0;				/** How many Watchdog Counter is reloaded */
static volatile ULONG gDD_PMC_Wdog_Remain_Number = 0;				/** Rest of Watchdog Counter divided by 0xFFFFFFFF multiple */
static volatile VP_CALLBACK	gDD_PMC_Wdog_Callback = NULL;			/** Pointer of Interrupt Callback Function */

/*----------------------------------------------------------------------*/
/* Macro																*/
/*----------------------------------------------------------------------*/
// Nothing Special

/*----------------------------------------------------------------------*/
/* Local Function														*/
/*----------------------------------------------------------------------*/
// Nothing Special

/*----------------------------------------------------------------------*/
/* Grobal Function														*/
/*----------------------------------------------------------------------*/
//---------------------------- driver section ----------------------------
/**
 * @brief  	Initialize Watchdog Register.
 */
VOID Dd_PMC_Wdog_Init(VOID)
{
	gDD_PMC_Wdog_Soft_Counter			= 0;
	gDD_PMC_Wdog_Remain_Number			= 0;
	
	// register write access enable.
	Dd_PMC_Wdog_Set_Lock(D_DD_PMC_WDOG_ENABLE_ON);
	// register write test disable.
	Dd_PMC_Wdog_Set_Test_Mode(D_DD_PMC_WDOG_ENABLE_OFF, D_DD_PMC_WDOG_ENABLE_OFF, D_DD_PMC_WDOG_ENABLE_OFF);

	// WdogControl
	IO_PMC.WATCHDOG.WdogControl.bit.RESEN	= D_DD_PMC_WDOG_ENABLE_OFF;		// disable the reset.
	IO_PMC.WATCHDOG.WdogControl.bit.INTEN	= D_DD_PMC_WDOG_ENABLE_OFF;		// disable the interrupt.
	// WdogLoad
//	IO_PMC.WATCHDOG.WdogLoad				= D_DD_PMC_WDOG_LOAD_INIT;		// Initialize Watchdog Load
}

/**
 * @brief	Setting of common control variable for Watchdog Counter.
 */
INT32 Dd_PMC_Wdog_Ctrl(T_DD_PMC_WDOG_CTRL* const wdog_ctrl)
{
#ifdef CO_PARAM_CHECK
	if( wdog_ctrl == NULL ){
		Ddim_Assertion(("Dd_PMC_Wdog_Ctrl: input param error. [*wdog_ctrl] NULL\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	// Set Callback function.
	gDD_PMC_Wdog_Callback		= wdog_ctrl->pIntCallback;

	Dd_PMC_Wdog_Set_Control(wdog_ctrl);							// Set value of WdogControl register
	Dd_PMC_Wdog_Set_Load(wdog_ctrl->wdog_load);					// Set value of WdogLoad register
	
	return D_DDIM_OK;
}

/**
 * @brief	Get value of WdogLoad register.
 */
INT32 Dd_PMC_Wdog_Get_Load(ULONG* wdog_load)
{
#ifdef CO_PARAM_CHECK
	if( wdog_load == NULL ){
		Ddim_Assertion(("Dd_PMC_Wdog_Get_Load: input param error. [*wdog_load] NULL\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	*wdog_load = IO_PMC.WATCHDOG.WdogLoad;							// Get value of WdogLoad register
	
	return D_DDIM_OK;
}

/**
 * @brief	Get value of WdogValue register.
 */
INT32 Dd_PMC_Wdog_Get_Counter(ULONG* wdog_counter)
{
#ifdef CO_PARAM_CHECK
	if( wdog_counter == NULL ){
		Ddim_Assertion(("Dd_PMC_Wdog_Get_Counter: input param error. [*wdog_counter] NULL\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	*wdog_counter	= IO_PMC.WATCHDOG.WdogValue;					// Get value of WdogValue register
	
	return D_DDIM_OK;
}

/**
 * @brief	Get value of WdogControl register.
 */
INT32 Dd_PMC_Wdog_Get_Control(T_DD_PMC_WDOG_CTRL* wdog_ctrl)
{
#ifdef CO_PARAM_CHECK
	if( wdog_ctrl == NULL ){
		Ddim_Assertion(("Dd_PMC_Wdog_Get_Control: input param error. [*wdog_ctrl] NULL\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	wdog_ctrl->reset_enable	= IO_PMC.WATCHDOG.WdogControl.bit.RESEN;			// Get value of reset enable.
	wdog_ctrl->int_enable	= IO_PMC.WATCHDOG.WdogControl.bit.INTEN;			// Get value of interrupt enable.
	
	return D_DDIM_OK;
}

/**
 * @brief	Get value of WdogRIS/WdogMIS register.
 */
INT32 Dd_PMC_Wdog_Get_Interrupt(UCHAR* raw_wdog_int, UCHAR* wdog_int)
{
#ifdef CO_PARAM_CHECK
	if((raw_wdog_int == NULL) || (wdog_int == NULL)){
		Ddim_Assertion(("Dd_PMC_Wdog_Get_Interrupt: input param error. [*wdog_interrupt] NULL\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	*raw_wdog_int	= IO_PMC.WATCHDOG.WdogRIS.bit.WDOGRIS;		// Get value of WdogRIS register
	*wdog_int		= IO_PMC.WATCHDOG.WdogMIS.bit.WDOGMIS;		// Get value of WdogMIS register
	
	return D_DDIM_OK;
}

/**
 * @brief	Get value of WdogLock register.
 */
INT32 Dd_PMC_Wdog_Get_Lock(UCHAR* wdog_lock)
{
#ifdef CO_PARAM_CHECK
	if( wdog_lock == NULL ){
		Ddim_Assertion(("Dd_PMC_Wdog_Get_Lock: input param error. [wdog_lock] NULL\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	// Get WdogLock
	*wdog_lock	= IO_PMC.WATCHDOG.WdogLock;
	
	return D_DDIM_OK;
}

/**
 * @brief	Get value of WdogITCR register.
 */
INT32 Dd_PMC_Wdog_Get_Test_Mode(UCHAR* test_mode)
{
#ifdef CO_PARAM_CHECK
	if( test_mode == NULL ){
		Ddim_Assertion(("Dd_PMC_Wdog_Get_Test_Mode: input param error. [test_mode] NULL\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	// Get WdogITCR.
	*test_mode	= IO_PMC.WATCHDOG.WdogITCR.bit.ITEN;
	
	return D_DDIM_OK;
}

/**
 * @brief	Get value of WdogPeriphID0~3 register.
 */
INT32 Dd_PMC_Wdog_Get_Peri_Identification(T_DD_PMC_WDOG_PERI_IDENTIFICATION* identification)
{
#ifdef CO_PARAM_CHECK
	if( identification == NULL ){
		Ddim_Assertion(("Dd_PMC_Wdog_Get_Peri_Identification: input param error. [identification] NULL\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	// Get WdogPeriphID0~3
	identification->part_number		= IO_PMC.WATCHDOG.WdogPeriphID0.bit.PartNumber0 | (IO_PMC.WATCHDOG.WdogPeriphID1.bit.PartNumber1 << 8);
	identification->designer		= IO_PMC.WATCHDOG.WdogPeriphID1.bit.Designer0 | (IO_PMC.WATCHDOG.WdogPeriphID2.bit.Designer1 << 4);
	identification->revision_number	= IO_PMC.WATCHDOG.WdogPeriphID2.bit.Revision;
	identification->configuration	= IO_PMC.WATCHDOG.WdogPeriphID3.bit.Configuration;
	
	return D_DDIM_OK;
}

/**
 * @brief	Get value of WdogPCellID0~3 register.
 */
INT32 Dd_PMC_Wdog_Get_PCell_Identification(ULONG* pcell)
{
#ifdef CO_PARAM_CHECK
	if( pcell == NULL ) {
		Ddim_Assertion(("Dd_PMC_Wdog_Get_PCell_Identification: input param error. [pcell] NULL\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	// Get WdogPCellID0~3
	*pcell	= IO_PMC.WATCHDOG.WdogPCellID[0].bit.WdogPCellID | (IO_PMC.WATCHDOG.WdogPCellID[1].bit.WdogPCellID << 8) |
				(IO_PMC.WATCHDOG.WdogPCellID[2].bit.WdogPCellID << 16) | (IO_PMC.WATCHDOG.WdogPCellID[3].bit.WdogPCellID << 24);

	return D_DDIM_OK;
}

/**
 * @brief	Set value of WdogLoad register.
 */
INT32 Dd_PMC_Wdog_Set_Load(ULONG wdog_load)
{
	IO_PMC.WATCHDOG.WdogLoad = wdog_load;					// Set value of WdogLoad register
	
	return D_DDIM_OK;
}

/**
 * @brief	Set value of WdogControl register.
 */
INT32 Dd_PMC_Wdog_Set_Control(T_DD_PMC_WDOG_CTRL* wdog_ctrl)
{
#ifdef CO_PARAM_CHECK
	if( wdog_ctrl == NULL ){
		Ddim_Assertion(("Dd_PMC_Wdog_Set_Control: input param error. [*wdog_ctrl] NULL\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	// Set WdogControl
	IO_PMC.WATCHDOG.WdogControl.bit.RESEN	= wdog_ctrl->reset_enable;
	IO_PMC.WATCHDOG.WdogControl.bit.INTEN	= wdog_ctrl->int_enable;
	
	return D_DDIM_OK;
}

/**
 * @brief	Clear Interrupt flag (Set WdogIntClr register).
 */
INT32 Dd_PMC_Wdog_Clear_Interrupt(VOID)
{
	IO_PMC.WATCHDOG.WdogIntClr = 1;			// Set value of WdogIntClr register
	
	return D_DDIM_OK;
}

/**
 * @brief	Set value of WdogLock register.
 */
INT32 Dd_PMC_Wdog_Set_Lock(UCHAR wdog_lock)
{
#ifdef CO_PARAM_CHECK
	if((wdog_lock != D_DD_PMC_WDOG_ENABLE_ON) && (wdog_lock != D_DD_PMC_WDOG_ENABLE_OFF)){
		Ddim_Assertion(("Dd_PMC_Wdog_Set_Lock: input param error. wdog_lock=%d\n", wdog_lock));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	if( wdog_lock == D_DD_PMC_WDOG_ENABLE_OFF ){
		// Unlock(write access to all other registers is enabled).
		IO_PMC.WATCHDOG.WdogLock	= D_DD_PMC_WDOG_REGISTER_ACCESS_ENABLE;
	}
	else{
		// Lock(write access to all other registers is disable).
		IO_PMC.WATCHDOG.WdogLock	= 0;
	}
	return D_DDIM_OK;
}

/**
 * @brief	Set value of WdogITCR/WdogITOP register.
 */
INT32 Dd_PMC_Wdog_Set_Test_Mode(UCHAR test_mode, UCHAR test_res, UCHAR test_int)
{
#ifdef CO_PARAM_CHECK
	if((test_mode != D_DD_PMC_WDOG_ENABLE_ON) && (test_mode != D_DD_PMC_WDOG_ENABLE_OFF)){
		Ddim_Assertion(("Dd_PMC_Wdog_Set_Test_Mode: input param error. test_mode=%d\n", test_mode));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
	if((test_res != D_DD_PMC_WDOG_ENABLE_ON) && (test_res != D_DD_PMC_WDOG_ENABLE_OFF)){
		Ddim_Assertion(("Dd_PMC_Wdog_Set_Test_Mode: input param error. test_res=%d\n", test_res));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
	if((test_int != D_DD_PMC_WDOG_ENABLE_ON) && (test_int != D_DD_PMC_WDOG_ENABLE_OFF)){
		Ddim_Assertion(("Dd_PMC_Wdog_Set_Test_Mode: input param error. test_int=%d\n", test_int));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	// Set WdogITCR.
	IO_PMC.WATCHDOG.WdogITCR.bit.ITEN		= test_mode;
	// Set WdogITOP.
	IO_PMC.WATCHDOG.WdogITOP.bit.WDOGRES	= test_res;
	IO_PMC.WATCHDOG.WdogITOP.bit.WDOGINT	= test_int;
	
	return D_DDIM_OK;
}

/**
 * @brief	It is Interrupt Handler of Watchdog.<br>
 *			The CallBack function is called.
*/
VOID Dd_PMC_Wdog_Int_Handler(VOID)
{
	// Soft Counter check
	if( gDD_PMC_Wdog_Soft_Counter > 1 ){
		gDD_PMC_Wdog_Soft_Counter--;
		
		// Clear Interrupt
		Dd_PMC_Wdog_Clear_Interrupt();
		Dd_ARM_Dsb_Pou();
		
		// Reload
		Dd_PMC_Wdog_Set_Load(D_DD_PMC_WDOG_COUNTER_MAX_VALUE);
	}
	else if( gDD_PMC_Wdog_Soft_Counter == 1 ){
		gDD_PMC_Wdog_Soft_Counter--;
		
		// Clear Interrupt
		Dd_PMC_Wdog_Clear_Interrupt();
		Dd_ARM_Dsb_Pou();
		
		// Reload
		Dd_PMC_Wdog_Set_Load(gDD_PMC_Wdog_Remain_Number);
	}
	else{
		if( gDD_PMC_Wdog_Callback != NULL ){
			// Callback.
			(*gDD_PMC_Wdog_Callback)();
		}
		
		// Clear Interrupt
		Dd_PMC_Wdog_Clear_Interrupt();
		Dd_ARM_Dsb_Pou();
	}
}

#ifdef CO_DDIM_UTILITY_USE
//---------------------------- utility section ---------------------------
/**
 * @brief  To input the value at the passed time to WdogLoad, it converts it into the number of clocks. 
 */
INT32 Dd_PMC_Wdog_Calculate(ULONG wdog_wdmode, ULONG msec, ULONG* convert_counter)
{
	ULLONG pclk;
	ULLONG msec_count;
	
#ifdef CO_PARAM_CHECK
	if( wdog_wdmode > 1 ){
		Ddim_Assertion(("Dd_PMC_Wdog_Calculate: input param error. wdog_wdmode > 1\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
	if( msec == 0 ){
		Ddim_Assertion(("Dd_PMC_Wdog_Calculate: input param error. [msec] = 0\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
	if( convert_counter == NULL ){
		Ddim_Assertion(("Dd_PMC_Wdog_Calculate: input param error. [convert_counter] = NULL\n"));
		return D_DD_PMC_WDOG_INPUT_PARAM_ERR;
	}
#endif
	if ( Dd_PMC_CRG11_Get_CRPLC_PLLBYPASS() ){
		// 32KHz.
		pclk = 32000 / (Dd_PMC_CRG11_Get_CRDM1_DIVMODE() + 1);
	}
	else {
		// 100MHz.
		pclk = 100000000 / (Dd_PMC_CRG11_Get_CRDM1_DIVMODE() + 1);
	}
	msec_count = pclk * msec / 1000;															// Convert to counter value
	if( wdog_wdmode == 1 ){
		// Watchdog mode
		if( msec_count > D_DD_PMC_WDOG_COUNTER_MAX_VALUE ){
			return D_DD_PMC_WDOG_OVERFLOW;
		}
	}
	
	gDD_PMC_Wdog_Soft_Counter = msec_count / ((ULLONG)D_DD_PMC_WDOG_COUNTER_MAX_VALUE + 1);		// Calculate to input value divided by the maximum value of 32bit
	gDD_PMC_Wdog_Remain_Number = msec_count & D_DD_PMC_WDOG_COUNTER_MAX_VALUE;					// Calculate to the remainder into which the input value is divided by the maximum value of 32bit is acquired
	if( gDD_PMC_Wdog_Soft_Counter > 0 ){
		msec_count = D_DD_PMC_WDOG_COUNTER_MAX_VALUE;											// Max value of watchdog counter
	}
	
	*convert_counter = msec_count;
	
	return D_DDIM_OK;
}

/**
 * @brief  The passed time to WdogLoad to set the watchdog timer, msec converts into the number of clocks. 
 */
INT32 Dd_PMC_Wdog_Set_Timer(ULONG wdog_wdmode, ULONG msec)
{
	ULONG convert_counter;
	INT32 ercd;
	
	ercd = Dd_PMC_Wdog_Calculate(wdog_wdmode, msec, &convert_counter);
	if( ercd != D_DDIM_OK ){
		return ercd;
	}
	
	Dd_PMC_Wdog_Set_Load(convert_counter);					// Set value of WdogLoad register
	
	return D_DDIM_OK;
}

#endif	// CO_DDIM_UTILITY_USE
